/*
 * EEPROM.cpp
 *
 *  Created on: Jan 16, 2014
 *      Author: ppks
 */
#include "common.h"
#include "main.h"
#include "avr/eeprom.h"
#include "FlightController.h"
#include "FlightController_Defaults.h"
#include "FlightController_Configuration.h"
#include "GPS_common.h"
#include "CRC.h"
#include "Alarms.h"
#include "EEPROM.h"

/******************************************************************************/
/* Local Variables Declaration                                                */
/******************************************************************************/


/******************************************************************************/
/* Local Functions Declaration                                                */
/******************************************************************************/


/******************************************************************************/
/* Function Code                                                              */
/******************************************************************************/
/*******************************************************************************
* Function Name: EEPROM_ReadConfigInfo
********************************************************************************
* Summary:
*  Read Config Information from the EEPROM to the ConfigInfo RAM structure
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_ReadConfigInfo(void)
{
    /* Read Global_Conf from EEPROM starting at address 0 */
    eeprom_read_block((void*)&ConfigInfo, (void*)EEPROM_GET_GLOBAL_INFO_OFFSET(), sizeof(ConfigInfo));
}

/*******************************************************************************
* Function Name: EEPROM_WriteConfigInfo
********************************************************************************
* Summary:
*  Writes Config Information to the EEPROM from the ConfigInfo RAM structure
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_WriteConfigInfo(void)
{
    /* Read Global_Conf from EEPROM starting at address 0 */
    eeprom_write_block((const void*)&ConfigInfo, (void*)EEPROM_GET_GLOBAL_INFO_OFFSET(), sizeof(ConfigInfo));
}

/*******************************************************************************
* Function Name: EEPROM_UpdateConfigInfo
********************************************************************************
* Summary:
*  The same functionality as write ConfigInfo, but it is used instead for EEPROM
*  endurance and only updates the values that are changed
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_UpdateConfigInfo(void)
{
    /* Read Global_Conf from EEPROM starting at address 0 */
    eeprom_update_block((const void*)&ConfigInfo, (void*)EEPROM_GET_GLOBAL_INFO_OFFSET(), sizeof(ConfigInfo));
}

/*******************************************************************************
* Function Name: EEPROM_ReadCalibrationData
********************************************************************************
* Summary:
*  Reads calibration data from flash. Returns false if the checksum does not match.
*
* Parameters:
*  None
*
* Return:
*  bool - 1: checksum correct
*         0: checksum error
*
*******************************************************************************/
bool EEPROM_ReadCalibrationData(void)
{
    /* Read Global_Conf from EEPROM starting at address 0 */
    eeprom_read_block((void*)&calData, (void*)EEPROM_GET_CAL_DATA_OFFSET(), sizeof(calData));

    if (CRC_CalcCrc16( (uint8_t*)&calData, sizeof(calData)-2) != calData.checksum)
    {
        /* If CRC doesn't match, return false flag */
        return false;
    }
    /* otherwise indicate that the data is correct in the EEPROM */
    return true;
}

/*******************************************************************************
* Function Name: EEPROM_WriteCalibrationData
********************************************************************************
* Summary:
*  This function writes calibration structure calData to EEPROM to address 0
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_WriteCalibrationData(void)
{
    /* Calc the checksum */
    calData.checksum = CRC_CalcCrc16( (uint8_t*)&calData, sizeof(calData)-2);

    /* Write data to EEPROM */
    eeprom_write_block((const void*)&calData, (void*)EEPROM_GET_CAL_DATA_OFFSET(), sizeof(calData));
}

/*******************************************************************************
* Function Name: EEPROM_UpdateCalibrationData
********************************************************************************
* Summary:
*  This function updates the data in EEPROM (only the data that is changed).
*  This replaces the write function to prolong EEPROM life.
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_UpdateCalibrationData(void)
{
    /* Calc the checksum */
    calData.checksum = CRC_CalcCrc16( (uint8_t*)&calData, sizeof(calData)-2);

    /* Write data to EEPROM */
    eeprom_update_block((const void*)&calData, (void*)EEPROM_GET_CAL_DATA_OFFSET(), sizeof(calData));
}

/*******************************************************************************
* Function Name: EEPROM_ReadParams
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
bool EEPROM_ReadParams(uint8_t configSet)
{
    /* Read EEPROM block */
    eeprom_read_block((void*)&FC_Config, (void*)EEPROM_GET_CONFIG_SET_OFFSET(configSet), sizeof(FC_Config));

    if (CRC_CalcCrc16((uint8_t*)&FC_Config, sizeof(FC_Config) - 2) != FC_Config.checksum)
    {
        /* Indicate an Error Status on LED */
        Alarms_BlinkLED(6, 100, 3);
    #if defined(BUZZER)
        Alarms_SetAlarmLevel(ALARM_EVENT_CONFIRMATION, ALARM_LEVEL_3);
    #endif /* defined(BUZZER) */
        /* force load defaults */
        FC_LoadConfigDefaults();
        return false;                   /* defaults loaded, don't reload constants (EEPROM life saving) */
    }

    return true;    /* setting is OK */
}

/*******************************************************************************
* Function Name: EEPROM_WriteParams
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_WriteParams(uint8_t configSet, uint8_t b)
{
    FC_Config.checksum = CRC_CalcCrc16((uint8_t*)&FC_Config, sizeof(FC_Config) - 2);
    eeprom_write_block((const void*)&FC_Config, (void*)EEPROM_GET_CONFIG_SET_OFFSET(configSet), sizeof(FC_Config));

    if (b == 1)
    {
        Alarms_BlinkLED(15, 20, 1);
    }
#if defined(BUZZER)
    Alarms_SetAlarmLevel(ALARM_EVENT_CONFIRMATION, ALARM_LEVEL_1);
#endif /* defined(BUZZER) */
}

/*******************************************************************************
* Function Name: EEPROM_UpdateParams
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_UpdateParams(uint8_t configSet, uint8_t b)
{
    FC_Config.checksum = CRC_CalcCrc16((uint8_t*)&FC_Config, sizeof(FC_Config) - 2);
    eeprom_update_block((const void*)&FC_Config, (void*)EEPROM_GET_CONFIG_SET_OFFSET(configSet), sizeof(FC_Config));

    if (b == 1)
    {
        Alarms_BlinkLED(15, 20, 1);
    }
#if defined(BUZZER)
    Alarms_SetAlarmLevel(ALARM_EVENT_CONFIRMATION, ALARM_LEVEL_1);
#endif /* defined(BUZZER) */
}

/*******************************************************************************
* Function Name: EEPROM_UpdateGPSConfig
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
bool EEPROM_ReadGPSConfig(void)
{
    /* Read EEPROM block */
    eeprom_read_block((void*)&GPS_Config, (void*)EEPROM_GET_GPS_CONFIG_OFFSET, sizeof(GPS_Config));

    if (CRC_CalcCrc16((uint8_t*)&GPS_Config, sizeof(GPS_Config) - 2) != GPS_Config.checksum)
    {
        /* force load defaults */
        GPS_LoadDefaultConfiguration();
        return false;
    }

    /* Perform a check if the correct WP max number is stored in EEPROM */
    if (GPS_Config.max_wp_number != EEPROM_GetMaxWaypointRecords())
    {
        GPS_Config.max_wp_number = EEPROM_GetMaxWaypointRecords();

        /* Update the configuration in EEPROM */
        EEPROM_WriteGPSConfig();
    }

    return true;    /* GPS config is OK */
}

/*******************************************************************************
* Function Name: EEPROM_WriteGPSConfig
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_WriteGPSConfig(void)
{
    /* Calculate Checksum */
    GPS_Config.checksum = CRC_CalcCrc16((uint8_t*)&GPS_Config, sizeof(GPS_Config) - 2);

    /* Write GPS Config to the EEPROM */
    eeprom_write_block( (void*)&GPS_Config, (void*)EEPROM_GET_GPS_CONFIG_OFFSET, sizeof(GPS_Config));
}

/*******************************************************************************
* Function Name: EEPROM_UpdateGPSConfig
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_UpdateGPSConfig(void)
{
    /* Calculate Checksum */
    GPS_Config.checksum = CRC_CalcCrc16((uint8_t*)&GPS_Config, sizeof(GPS_Config) - 2);

    /* Write GPS Config to the EEPROM */
    eeprom_update_block( (void*)&GPS_Config, (void*)EEPROM_GET_GPS_CONFIG_OFFSET, sizeof(GPS_Config));
}

/*******************************************************************************
* Function Name: EEPROM_GetMaxWaypointRecords
********************************************************************************
* Summary:
*  This function calculates how much space is available in the EEPROM and based
*  on one waypoint record size returns number of possible records that can fit
*
* Parameters:
*  None
*
* Return:
*  uint8 - maximum number of waypoints that can fit in available EEPROM
*
*******************************************************************************/
uint8_t EEPROM_GetMaxWaypointRecords(void)
{
    uint16_t maxWPNum = (EEPROM_LAST_AVAILABLE_BYTE - EEPROM_GET_WP_DATA_OFFSET) / sizeof(WaypointRecord);
    if (maxWPNum > NAV_MAX_WAYPOINTS_NUM)
    {
        maxWPNum = NAV_MAX_WAYPOINTS_NUM;
    }

    return (uint8_t)maxWPNum;
}

/*******************************************************************************
* Function Name: EEPROM_ReadWPRecord
********************************************************************************
* Summary:
*  Reads the WP record from EEPROM. If checksum doesn't match or if the record
*  index is greater than maximum possible it returns false
*
* Parameters:
*  uint8_t recordIndex - WP number to be read from EEPROM
*
* Return:
*  bool - indicates the success or not of reading the waypoint
*
*******************************************************************************/
bool EEPROM_ReadWPRecord(uint8_t recordIndex)
{
    if (recordIndex > EEPROM_GetMaxWaypointRecords())
    {
        return false;
    }

    /* Read record from EEPROM */
    eeprom_read_block((void*)&WaypointRecord, (void*)(EEPROM_GET_WP_RECORD_OFFSET(recordIndex)), sizeof(WaypointRecord));

    /* Check the record integrity */
    if (CRC_CalcCrc8((uint8_t*)&WaypointRecord, sizeof(WaypointRecord) - 1) != WaypointRecord.checksum)
    {
        return false;
    }
    return true;
}

/*******************************************************************************
* Function Name: EEPROM_WriteWPRecord
********************************************************************************
* Summary:
*  Writes the waypoint record to EEPROM. It does not have paramters as index as
*  the waypoint record has its number in the body
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void EEPROM_WriteWPRecord(void)
{
    if (WaypointRecord.number > EEPROM_GetMaxWaypointRecords())
    {
        return;
    }

    /* Calc checksum */
    WaypointRecord.checksum = CRC_CalcCrc8((uint8_t*)&WaypointRecord, sizeof(WaypointRecord) - 1);

    /* Write block to EEPROM */
    eeprom_write_block((void*)&WaypointRecord, (void*)(EEPROM_GET_WP_RECORD_OFFSET(WaypointRecord.number)), sizeof(WaypointRecord));
}
